import { HonoContext } from '../types/hono';
import { AppError } from '../utils/error';
import { User } from '../types/db';
import { generateToken } from '../handlers/auth';
import { StateManager } from '../utils/stateManager';
import { generateId } from '../utils/id-generator';


/**
 * OAuth用户信息接口
 */
interface OAuthUserInfo {
  /** 第三方平台的用户ID */
  id: string;
  /** 用户邮箱 */
  email: string;
  /** 用户名称 */
  name: string;
}

/**
 * OAuth认证服务类
 * 处理第三方登录相关的业务逻辑
 */
export class OAuthService {
  constructor(private readonly context: HonoContext) { }

  /**
   * 生成OAuth授权URL
   * @param provider - OAuth提供商（如：'google'）
   * @returns 完整的授权URL
   * @throws {AppError} 当提供商不支持时抛出错误
   */
  async generateAuthUrl(provider: string): Promise<string> {
    if (provider !== 'google') {
      throw new AppError('Unsupported OAuth provider');
    }

    const state = await StateManager.generate(this.context.env.AES_KEY);
    const OAUTH_CONFIG = this.context.env.OAUTH_CONFIG;

    const params = new URLSearchParams({
      client_id: OAUTH_CONFIG.google.clientId,
      redirect_uri: `${this.context.env.APP_URL}/api/oauth/${provider}/callback`,
      response_type: 'code',
      scope: OAUTH_CONFIG.google.scope,
      state: state,
      access_type: 'offline',
      prompt: 'consent'
    });

    return `${OAUTH_CONFIG.google.authUrl}?${params.toString()}`;
  }

  /**
   * 处理OAuth回调
   * @param provider - OAuth提供商
   * @param code - 授权码
   * @param state - 状态参数
   * @returns JWT token
   * @throws {AppError} 当验证失败或处理出错时抛出错误
   */
  async handleCallback(provider: string, code: string, state: string): Promise<string> {
    // 验证 state
    const isValidState = await StateManager.validate(state, this.context.env.AES_KEY);
    if (!isValidState) {
      throw new AppError("Invalid state parameter");
    }

    if (provider !== 'google') {
      throw new AppError('Unsupported OAuth provider');
    }

    // 2. 使用code获取access token
    const tokenData = await this.getAccessToken(provider, code);

    // 3. 获取用户信息
    const userInfo = await this.getUserInfo(provider, tokenData.access_token);

    // 4. 查找或创建用户
    const user = await this.findOrCreateUser(provider, userInfo);

    // 5. 生成JWT token
    return generateToken(this.context, user.id);
  }

  /**
   * 获取OAuth访问令牌
   * @private
   * @param provider - OAuth提供商
   * @param code - 授权码
   * @returns 包含access_token的对象
   * @throws {AppError} 当请求失败时抛出错误
   */
  private async getAccessToken(provider: string, code: string) {
    const tokenUrl = this.getTokenUrl(provider);
    const response = await fetch(tokenUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },

      body: new URLSearchParams({
        client_id: this.context.env.OAUTH_CONFIG.google.clientId,
        client_secret: this.context.env.OAUTH_CONFIG.google.clientSecret,
        code,
        grant_type: 'authorization_code',
        redirect_uri: `${this.context.env.APP_URL}/api/oauth/${provider}/callback`,
      }),
    });

    if (!response.ok) {
      const error = await response.text();
      console.error('Token request failed:', error);
      throw new AppError('Failed to get access token');
    }

    return response.json<{ access_token: string }>();
  }

  /**
   * 获取用户信息
   * @private
   * @param provider - OAuth提供商
   * @param accessToken - 访问令牌
   * @returns 用户信息
   * @throws {AppError} 当请求失败时抛出错误
   */
  private async getUserInfo(provider: string, accessToken: string): Promise<OAuthUserInfo> {
    const userInfoUrl = this.getUserInfoUrl(provider);
    const response = await fetch(userInfoUrl, {
      headers: {
        'Authorization': `Bearer ${accessToken}`,
      },
    });

    if (!response.ok) {
      throw new AppError('Failed to get user info');
    }

    const data = await response.json();
    return this.parseUserInfo(provider, data);
  }

  private async findOrCreateUser(provider: string, userInfo: OAuthUserInfo): Promise<User> {
    // 查找现有OAuth账号
    const existingAccount = await this.context.env.DB.prepare(`
      SELECT user_id FROM oauth_accounts 
      WHERE provider_name = ? AND provider_user_id = ?
    `).bind(provider, userInfo.id).first<{ user_id: string }>();

    if (existingAccount) {
      const user = await this.context.env.DB.prepare(
        'SELECT * FROM users WHERE id = ?'
      ).bind(existingAccount.user_id).first<User>();

      if (!user) {
        throw new AppError('User not found');
      }

      return user;
    }

    // 使用新的 ID 生成方法
    const userId = generateId();
    
    // 创建新用户和OAuth账号
    const addUser = this.context.env.DB.prepare(`
      INSERT INTO users (id, username, email, password_hash) 
      VALUES (?, ?, ?, '')
    `).bind(
      userId,
      userInfo.name ? userInfo.name : `user_${Date.now()}`,
      userInfo.email,
    )

    const addOAuthAccount = this.context.env.DB.prepare(`
      INSERT INTO oauth_accounts (user_id, provider_name, provider_user_id)
      VALUES (?, ?, ?)
    `).bind(userId, provider, userInfo.id);

    // 事务执行
    await this.context.env.DB.batch([addUser, addOAuthAccount]);

    const user = await this.context.env.DB.prepare(
      'SELECT * FROM users WHERE id = ?'
    ).bind(userId).first<User>();

    if (!user) {
      throw new AppError('Failed to retrieve created user');
    }

    return user;
  }

  private getTokenUrl(provider: string): string {
    switch (provider) {
      case 'google':
        return 'https://oauth2.googleapis.com/token';
      default:
        throw new AppError('Unsupported provider');
    }
  }

  private getUserInfoUrl(provider: string): string {
    switch (provider) {
      case 'google':
        return 'https://www.googleapis.com/oauth2/v2/userinfo';
      default:
        throw new AppError('Unsupported provider');
    }
  }

  private parseUserInfo(provider: string, data: any): OAuthUserInfo {
    switch (provider) {
      case 'google':
        return {
          id: data.id,
          email: data.email,
          name: data.name,
        };
      default:
        throw new AppError('Unsupported provider');
    }
  }
} 